package com.kz.realm;

import com.kz.ShiroCasConfiguration;
import com.kz.entity.sys.SysUser;
import com.kz.remote.vo.SysRoleFunctionVO;
import com.kz.userservice.SysRoleFunctionRpcService;
import com.kz.userservice.SysRoleUserRpcService;
import com.kz.userservice.SysUserRpcService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cas.CasRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

import javax.annotation.PostConstruct;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author kz
 */
public class MyShiroRealm extends CasRealm {

    private static final Logger logger = LoggerFactory.getLogger(MyShiroRealm.class);


    @Autowired(required = false)
    private SysUserRpcService sysUserRpcService;

    @Autowired(required = false)
    private SysRoleUserRpcService sysRoleUserRpcService;

    @Autowired(required = false)
    private SysRoleFunctionRpcService sysRoleFunctionRpcService;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    /**
     * 用户登录次数计数  redisKey 前缀
     */
    private String SHIRO_LOGIN_COUNT = "shiro_login_count_";

    /**
     * 用户登录是否被锁定    一小时 redisKey 前缀
     */
    private String SHIRO_IS_LOCK = "shiro_is_lock_";

    private static final String LOCK = "LOCK";

    private static final Integer MAX_LOGIN = 5;


    @PostConstruct
    public void initProperty() {
        setDefaultRoles("ROLE_USER");
        setCasServerUrlPrefix(ShiroCasConfiguration.casServerUrlPrefix);
        // 客户端回调地址
        setCasService(ShiroCasConfiguration.shiroServerUrlPrefix + ShiroCasConfiguration.casFilterUrlPattern);
    }

    /**
     * 认证信息.(身份验证) : Authentication 是用来验证用户身份
     *
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        String username = token.getUsername();
        String password = String.valueOf(token.getPassword());
        //访问一次，计数一次
        ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
        opsForValue.increment(SHIRO_LOGIN_COUNT + username, 1);
        //计数大于5时，设置用户被锁定一小时
        if (Integer.parseInt(opsForValue.get(SHIRO_LOGIN_COUNT + username)) >= MAX_LOGIN) {
            opsForValue.set(SHIRO_IS_LOCK + username, "LOCK");
            stringRedisTemplate.expire(SHIRO_IS_LOCK + username, 1, TimeUnit.HOURS);
        }
        if (LOCK.equals(opsForValue.get(SHIRO_IS_LOCK + username))) {
            throw new DisabledAccountException("由于密码输入错误次数大于5次，帐号已经禁止登录！");
        }
        // 从数据库获取对应用户名密码的用户
        SysUser user = sysUserRpcService.selectOne(username, password);
        if (null == user) {
            throw new AccountException("帐号或密码不正确！");
        } else if (!StringUtils.equals("0", user.getStatus().toString())) {
            /**
             * 如果用户的status为禁用。那么就抛出<code>DisabledAccountException</code>
             */
            throw new DisabledAccountException("此帐号已经设置为禁止登录！");
        } else {
            //登录成功
            //更新登录时间 last login time
            user.setLastUpdate(new Date());
            sysUserRpcService.update(user);
            //清空登录计数
            opsForValue.set(SHIRO_LOGIN_COUNT + username, "0");
        }
        logger.info("身份认证成功，登录用户：" + username);
        return new SimpleAuthenticationInfo(user, password, getName());
    }

    /**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("权限认证方法：MyShiroRealm.doGetAuthorizationInfo()");
        SysUser user = (SysUser) SecurityUtils.getSubject().getPrincipal();
        String userId = user.getId();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //根据用户ID查询角色（role），放入到Authorization里。
        String roleId = sysRoleUserRpcService.getRoleIdByUserId(userId);
        Set<String> roleSet = new HashSet<String>();
        roleSet.add(roleId);

        //根据用户ID查询权限（permission），放入到Authorization里。
        List<SysRoleFunctionVO> permissionList = sysRoleFunctionRpcService.queryRoleFunctionList(roleId);
        Set<String> permissionSet = new HashSet<String>();
        for (SysRoleFunctionVO functionVO : permissionList) {
            permissionSet.add(functionVO.getFunctionUrl());
        }
        info.setStringPermissions(permissionSet);
        return info;
    }


    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof UsernamePasswordToken;
    }

}
